/*
s_axis_tuser:
[79:64]icmp_seq
[63:48]icmp_id
[47:16] rx_ip_source_ip
[15:0]icmp_data_len

m_axis_tuser:
[55:24]---tx_ip_target_ip
[23:16]---tx_pkg_type  icmp=1
[15:0]---tx_ip_data_len
*/


module icmp_tx (
    input                       i_clk                       ,
    input                       i_rst                       ,
    input                       s_axis_tvalid                ,
    input  [7 : 0]              s_axis_tdata                 ,
    input                       s_axis_tlast                 ,
    input  [79: 0]              s_axis_tuser                 ,
    output                      m_axis_tvalid                ,
    output [7 : 0]              m_axis_tdata                 ,
    input                       m_axis_tready                ,
    output                      m_axis_tlast                 ,
    output [55: 0]              m_axis_tuser                   
);

    localparam  ICMP_PKG_TYPE     = 8'd1                    ;

    logic   [15: 0]             icmp_id                     ;
    logic   [15: 0]             icmp_seq                    ;
    logic   [15: 0]             icmp_pkg_len                ;
    logic   [15: 0]             icmp_2bytes                 ;
    logic                       icmp_2bytes_valid           ;
    logic   [7 : 0]             icmp_data                   ;
    logic                       icmp_last                   ;
    logic                       icmp_valid                  ;
    logic                       icmp_valid_delay               ;
    logic   [7 : 0]             axis_tx_tdata               ;
    logic   [55: 0]             axis_tx_tuser               ;
    logic                       axis_tx_tlast               ;
    logic                       axis_tx_tvalid              ;
    logic   [15: 0]             lsm_cnt                     ;
    logic                       fifo_rden                   ;
    logic                       send_req                    ;
    logic   [31: 0]             check_sum                   ;
    logic   [7 : 0]             fifo_rddata                 ;
    logic                       fifo_almost_full            ;
    logic                       fifo_empty                  ;


    assign m_axis_tdata  = axis_tx_tdata      ;
    assign m_axis_tuser  = axis_tx_tuser      ;
    assign m_axis_tlast  = axis_tx_tlast      ;
    assign m_axis_tvalid = axis_tx_tvalid     ;

    always_ff @(posedge i_clk) begin
        if(i_rst) begin
            icmp_last  <= 0;
            icmp_valid <= 0;
            icmp_valid_delay <= 0;
        end 
        else begin
            icmp_last  <= s_axis_tlast ;
            icmp_valid <= s_axis_tvalid;
            icmp_valid_delay <= icmp_valid;
        end
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            icmp_data  <= 0;
        else if(s_axis_tvalid)
            icmp_data  <= s_axis_tdata ;
        else 
            icmp_data <= 0;
    end

    sfifo_8x64 sfifo_8x64_inst2 (
        .clk                ( i_clk                 ),
        .srst               ( i_rst                 ),
        .din                ( icmp_data             ),
        .wr_en              ( icmp_valid            ),
        .rd_en              ( fifo_rden             ),
        .dout               ( fifo_rddata           ),
        .almost_full        ( fifo_almost_full      ),
        .full               ( fifo_full             ),
        .empty              ( fifo_empty            ) 
    );


     always_ff @(posedge i_clk) begin
        if(i_rst) begin
            icmp_id <= 0;
            icmp_seq <= 0;
            icmp_pkg_len <= 0;
        end 
        else if(s_axis_tvalid) begin
            icmp_id <= s_axis_tuser[63:48];
            icmp_seq <= s_axis_tuser[79:64];
            icmp_pkg_len <=  s_axis_tuser[15: 0] + 8;
        end 
        else begin
            icmp_id <= icmp_id;
            icmp_seq <= icmp_seq;
            icmp_pkg_len <= icmp_pkg_len;
        end
    end


    always_ff @(posedge i_clk) begin
        if(i_rst)
            check_sum <= 0;
        else if(icmp_valid & !icmp_valid_delay)
            check_sum <= icmp_id + icmp_seq;
        else if(icmp_2bytes_valid)
            check_sum <= check_sum + icmp_2bytes;
        else if(send_req & !axis_tx_tvalid)
            check_sum <= check_sum[31:16] + check_sum[15:0];
        else if(lsm_cnt == 1)   
            check_sum <= ~check_sum;
        else 
            check_sum <= check_sum;
    end  

    always_ff @(posedge i_clk) begin
        if(i_rst)
            icmp_2bytes <= 0;
        else 
            icmp_2bytes <= {icmp_2bytes[7 :0],icmp_data};
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            icmp_2bytes_valid <= 0;
        else if(icmp_2bytes_valid & icmp_valid_delay)
            icmp_2bytes_valid <= 0;
        else if(~icmp_2bytes_valid & icmp_valid_delay)
            icmp_2bytes_valid <= 1;
        else 
            icmp_2bytes_valid <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            send_req <= 0;
        else if(lsm_cnt == icmp_pkg_len - 1 )
            send_req <= 0;
        else if(!icmp_valid & icmp_valid_delay)   
            send_req <= 1;
        else 
            send_req <= send_req;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            lsm_cnt <= 0;
        else if(lsm_cnt == icmp_pkg_len - 1)
            lsm_cnt <= 0;
        else if(send_req && m_axis_tready)
            lsm_cnt <= lsm_cnt + 1;
        else 
            lsm_cnt <= lsm_cnt;
    end 

    always_ff @(posedge i_clk) begin
        if(i_rst)
            axis_tx_tdata <= 0;
        else case(lsm_cnt)
            0       :axis_tx_tdata <= 'd0;//ping ack
            1       :axis_tx_tdata <= 'd0;//ping ack
            2       :axis_tx_tdata <= check_sum[15: 8];
            3       :axis_tx_tdata <= check_sum[7 :0];
            4       :axis_tx_tdata <= icmp_id[15: 8];
            5       :axis_tx_tdata <= icmp_id[7 : 0];
            6       :axis_tx_tdata <= icmp_seq[15: 8];
            7       :axis_tx_tdata <= icmp_seq[7 : 0];
            default :axis_tx_tdata <= fifo_rddata;
        endcase
    end
    
    always_ff @(posedge i_clk) begin
        if(i_rst)
            fifo_rden <= 0;
        else if(lsm_cnt >= 7 && lsm_cnt < icmp_pkg_len - 1 && axis_tx_tvalid && m_axis_tready)
            fifo_rden <= 1;
        else 
            fifo_rden <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            axis_tx_tuser <= 0;
        else    
            axis_tx_tuser <= {s_axis_tuser[47:16],ICMP_PKG_TYPE, icmp_pkg_len};//type=1 :icmp;len = 40
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            axis_tx_tlast <= 0;
        else if(lsm_cnt == icmp_pkg_len - 1)
            axis_tx_tlast <= 1;
        else 
            axis_tx_tlast <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)   
            axis_tx_tvalid <= 0;
        else if(axis_tx_tvalid & axis_tx_tlast)
            axis_tx_tvalid <= 0;
        else if(send_req)
            axis_tx_tvalid <= 1;
        else 
            axis_tx_tvalid <= axis_tx_tvalid;
    end

endmodule
